package org.bookshare.document.beans;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.JDOMException;

/**
 * A set of files that together make up a document. Some of these files are generally accessible at JDOM documents.
 * @author Reuben Firmin
 *
 * @param <T> The type of document set.
 */
public interface DocumentSet<T extends DocumentType> {

	/**
	 * Get document type.
	 * @return Never null
	 */
	DocumentComponent[] getDocumentElements();

    /**
     * Get the type of the document set.
     * @return Never null
     */
    T getType();

    /**
     * Get the list of documents of this type.
     * @param documentType The document type.
     * @return Empty if none, never null
     */
    List<Document> getDocuments(DocumentComponent documentType);

    /**
     * Get *the* document of this specific type. May be null if it doesn't exist or hasn't been created. If there are
     * multiple documents of this type, then return the first.
     * @param documentType The type of document
     * @return Maybe null
     */
    Document getDocument(DocumentComponent documentType);

    /**
     * Add one or more files to this documentset.
     * {@link #createDocuments(DocumentType, EventListener, ViolationCollatingErrorHandler)} should be used after
     * all files of this type have been added to create the matching documents.
     * @param documentType The type of document
     * @param files One or more files to add
     * @throws IOException if the character encoding couldn't be verified or fixed
     */
    void addFiles(DocumentComponent documentType, File... files)
    throws IOException;

    /**
     * Add one or more files to this documentset.
     * {@link #createDocuments(DocumentType, EventListener, ViolationCollatingErrorHandler)} should be used after
     * all files of this type have been added to create the matching documents.
     * @param documentType The type of document
     * @param files One or more files to add
     * @throws IOException if the character encoding couldn't be verified or fixed
     */
    void addFiles(DocumentComponent documentType, List<File> files)
    throws IOException;

    /**
     * Overwrite the files mapped to this type. This will also clear documents created for this type.
     * {@link #createDocuments(DocumentType, EventListener, ViolationCollatingErrorHandler)} should be used after
     * all files of this type have been added to create the matching documents.
     * @param documentType The document type to overwrite file mappings for.
     * @param files The files.
     * @throws IOException If there were encoding issues
     */
	void overwriteFiles(final DocumentComponent documentType, final File... files)
	throws IOException;

    /**
     * Get the list of files of this type.
     * @param documentType The document type.
     * @return Empty if none, never null
     */
    List<File> getFiles(DocumentComponent documentType);

    /**
     * Get *the* file of this specific type. May be null if it doesn't exist. If there are multiple files of this type,
     * then return the first.
     * @param documentType The type of document
     * @return Maybe null
     */
    File getFile(DocumentComponent documentType);

    /**
     * Get the document derived from this file.
     * @param file never null
     * @return null if not found
     */
    Document getDocument(File file);

    /**
     * Get the document whose filename ends with the given string. If the string contains \ or /, then only the tail
     * of the string will be used for matching.
     * @param href never null
     * @return null if not found
     */
    Document getDocument(String href);

    /**
     * Get the file that this document was derived from. This may be less efficient that the companion
     * {@link #getDocument(File)}
     * @param document never null
     * @return null if not found
     */
    File getFile(Document document);

    /**
     * Whether documents exist yet (i.e. whether the files have been parsed).
     * @param type the type of documents being processed.
     * @return True if the documents have been created.
     */
    boolean hasBeenParsed(DocumentComponent type);

    /**
     * Get the base path that the files are located in.
     * @return never null
     */
    File getFileBasePath();

    /**
     * Get the name that can be used to represent this document set.
     * @return Null if it can't be derived.
     * @throws IOException if file access fails
     */
    String getName() throws IOException;

    /**
     * Create the documents from the files in this DocumentSet.
     * @param documentType The document type to parse
     * @param force Force parsing, even if the documents for the given type have already been created; i.e. overrides.
     * @throws JDOMException if JDOM breaks
     * @throws IOException if file access fails
     */
    void createDocuments(final DocumentComponent documentType, final boolean force)
    throws JDOMException, IOException;

    /**
     * True if this file is contained within the document set.
     * @param file The filename
     * @return True or false
     */
    boolean containsFile(String file) throws IOException;

    /**
     * True if this file is contained within the document set, ignoring case.
     * @param file The filename
     * @return True or false
     */
    boolean containsFileIgnoreCase(String file) throws IOException;

    /**
     * Files outwith the core files defined by the type that are also present in this document package.
     * @return Never null
     */
    List<File> getOtherFiles();
}
